package com.napenalki.toto.dao.jpa;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.napenalki.toto.dao.PredictionDao;
import com.napenalki.toto.domain.Event;
import com.napenalki.toto.domain.Match;
import com.napenalki.toto.domain.Prediction;
import com.napenalki.toto.domain.SpecialEvent;

@Transactional
@Repository
public class JpaPredictionDao implements PredictionDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Prediction addPrediction(Prediction prediction) {
        entityManager.persist(prediction);
        return prediction;
    }

    @Override
    public Prediction findPredictionById(int id) {
        return entityManager.find(Prediction.class, id);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Prediction> findAllMatchPredictions() {

        return substractEvents(getPredictionQuery().getResultList(),
                Match.class);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Prediction> findAllSpecialPredictions() {

        return substractEvents(getPredictionQuery().getResultList(),
                SpecialEvent.class);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Prediction> findPredictionsByEvent(Event event) {
        Query query = entityManager
                .createQuery("SELECT p FROM Prediction p WHERE p.event = :event");
        query.setParameter("event", event);
        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Prediction> findPredictionsByUser(String userName) {
        Query query = entityManager
                .createQuery("SELECT p FROM Prediction p WHERE p.user.userName "
                        + "= :userName");
        query.setParameter("userName", userName);
        return query.getResultList();
    }

    @Override
    public Prediction findPredictionByUserAndEvent(String userName, Event event) {
        Query query = entityManager.createQuery("SELECT p FROM Prediction p "
                + "WHERE p.user.userName = :userName AND p.event = :event");
        query.setParameter("userName", userName);
        query.setParameter("event", event);
        return (Prediction) query.getSingleResult();
    }

    @Override
    public Prediction updatePrediction(Prediction prediction) {
        try {
            Prediction predictionx = findPredictionByUserAndEvent(prediction
                    .getUser().getUserName(), prediction.getEvent());
            predictionx.setPrediction(prediction.getPrediction());
            entityManager.merge(predictionx);
            return predictionx;
        } catch (NoResultException nre) {
            entityManager.persist(prediction);
            return prediction;
        }
    }

    @Override
    public void deletePrediction(Prediction prediction) {
        try {
            Prediction predictionx = findPredictionByUserAndEvent(prediction
                    .getUser().getUserName(), prediction.getEvent());
            entityManager.remove(predictionx);
        } catch (NoResultException nre) {
            // Nothing to do -> log info
        }
    }

    private Query getPredictionQuery() {
        return entityManager.createQuery("SELECT p FROM Prediction p");
    }

    private List<Prediction> substractEvents(List<Prediction> allPredictions,
            Class<? extends Event> eventClass) {
        List<Prediction> result = new ArrayList<Prediction>(allPredictions
                .size());

        for (Prediction prediction : allPredictions) {
            if (prediction.getEvent().getClass().equals(eventClass)) {
                result.add(prediction);
            }
        }

        return result;
    }

}
